﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
\* ######################################## */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AJRavindiran.Jolt.RuneScape.Models.Characters.Information
{
    /// <summary>
    /// Represents a character's location on the world map.
    /// </summary>
    public class Location
    {
        #region Fields
        private int x, y, z;
        #endregion Fields

        #region Properties
        /// <summary>
        /// Gets or sets the characters current X coordinate.
        /// </summary>
        public int X
        {
            get { return this.x; }
            set { this.x = value; }
        }

        /// <summary>
        /// Gets or sets the characters current Y coordinate.
        /// </summary>
        public int Y
        {
            get { return this.y; }
            set { this.y = value; }
        }

        /// <summary>
        /// Gets or sets the characters current Z coordinate.
        /// </summary>
        public int Z
        {
            get { return this.z; }
            set { this.z = value; }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// Constructs a new location for a character.
        /// </summary>
        /// <param name="x">The X coordinate to place the character.</param>
        /// <param name="y">The Y coordinate to place the character.</param>
        /// <param name="z">The Z coordinate to place the character.</param>
        private Location(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        #endregion Constructors

        #region Methods
        public int GetLocalX()
        {
            return this.x - 8 * (GetRegionX() - 6);
        }

        public int GetLocalY()
        {
            return this.y - 8 * (GetRegionY() - 6);
        }

        public int GetLocalX(Location location)
        {
            return this.x - 8 * (location.GetRegionX() - 6);
        }

        public int GetLocalY(Location location)
        {
            return this.y - 8 * (location.GetRegionY() - 6);
        }

        public int GetRegionX()
        {
            return (this.x >> 3);
        }

        public int GetRegionY()
        {
            return (this.y >> 3);
        }

        public static Location New(int x, int y, int z)
        {
            return new Location(x, y, z);
        }

        public override int GetHashCode()
        {
            return this.z << 30 | x << 15 | this.y;
        }

        public override bool Equals(object other)
        {
            if (!(other is Location))
                return false;
            Location location = (Location)other;
            return location.x == x && location.y == y && location.z == z;
        }

        public override string ToString()
        {
            return string.Format("[X: {0}, Y: {1}, Z: {2}]", x, y, z);
        }

        public bool WithinDistance(Location other, int distance)
        {
            if (other.z != z)
                return false;
            int deltaX = other.x - x, deltaY = other.y - y;
            return (deltaX <= (distance) && deltaX >= (0 - distance - 1) 
                && deltaY <= (distance) && deltaY >= (0 - distance - 1));
        }

        public bool WithinDistance(Location other)
        {
            if (other.z != z)
                return false;
            int deltaX = other.x - x, deltaY = other.y - y;
            return deltaX <= 14 && deltaX >= -15 && deltaY <= 14 && deltaY >= -15;
        }

        public bool WithinInteractionDistance(Location location)
        {
            return this.WithinDistance(location, 3);
        }

        public static int WildernessLevel(Location location)
        {
            int y = location.y;
            if (y > 3520 && y < 4000)
            {
                return (((int)(Math.Ceiling((double)(y) - 3520D) / 8D) + 1));
            }
            return 0;
        }

        public double GetDistance(Location other)
        {
            int xdiff = this.x - other.x;
            int ydiff = this.y - other.y;
            return Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
        }
        #endregion Methods
    }
}
